package com.cargohu.aishouyu.auth.interceptor;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.cargohu.aishouyu.auth.annotation.IgnoreLogin;
import com.cargohu.aishouyu.auth.annotation.Permission;
import com.cargohu.aishouyu.auth.exception.IllegalOperationException;
import com.cargohu.aishouyu.auth.exception.LoginException;
import com.cargohu.aishouyu.auth.exception.LoginTokenException;
import com.cargohu.aishouyu.auth.service.MobileLoginRedisService;
import com.cargohu.aishouyu.auth.util.LoginUtil;
import com.cargohu.aishouyu.auth.util.ServletUtils;
import com.cargohu.aishouyu.auth.util.TokenUtil;
import com.cargohu.aishouyu.auth.vo.LoginRedisVo;
import com.cargohu.aishouyu.auth.vo.LoginVo;
import com.cargohu.aishouyu.auth.vo.MemberLoginRedisVo;
import com.cargohu.aishouyu.common.CommonConstants;
import com.cargohu.aishouyu.common.enums.PlatformType;
import com.cargohu.aishouyu.common.enums.TokenLengthType;
import com.cargohu.aishouyu.config.properties.LoginProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author aaron.wang
 * @date 2022/6/26
 **/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private LoginProperties loginProperties;
    @Autowired
    private MobileLoginRedisService mobileLoginRedisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String platformType = ServletUtils.getRequest().getHeader(CommonConstants.PLATFORM_TYPE);
        if (StrUtil.isEmpty(platformType)) {
            throw new IllegalOperationException(request.getRequestURI()+"请求违法!");
        }
        if (excludeRequest(request)){
            return Boolean.FALSE;
        }
        if (PlatformType.PC.getCode().equals(platformType)) {
            // 如果访问的是控制器
            if (handler instanceof HandlerMethod) {
                // 排除不需要登录验证的路径 路径配置 注解
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                IgnoreLogin ignoreLogin = handlerMethod.getMethodAnnotation(IgnoreLogin.class);
                if (ignoreLogin != null) {
                    return true;
                }
                ignoreLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(IgnoreLogin.class);
                if (ignoreLogin != null) {
                    return true;
                }
                String token = TokenUtil.getToken(request);
                if (StringUtils.isBlank(token)) {
                    throw new LoginTokenException("token不能为空");
                }
                if (token.length() != TokenLengthType.PC.getLength()) {
                    throw new LoginTokenException("无效token");
                }
                // 获取登录用户信息
                LoginRedisVo loginRedisVo = LoginUtil.getLoginRedisVo();
                if (loginRedisVo == null) {
                    throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
                }
                LoginVo loginVo = loginRedisVo.getLoginVo();
                List<String> roleCodes = loginVo.getRoleCodes();
                boolean loginPermission = loginProperties.isLoginPermission();
                if (loginPermission) {
                    Permission permission = handlerMethod.getMethodAnnotation(Permission.class);
                    if (permission != null) {
                        // 从redis中获取权限列表
                        List<String> permissions = loginRedisVo.getPermissions();
                        if (CollectionUtils.isEmpty(permissions)) {
                            throw new LoginException("当前用户未设置权限");
                        }
                        String value = permission.value();
                        String role = permission.role();
                        String[] roles = permission.roles();
                        if (!permissions.contains(value)) {
                            log.error("没有访问权限的登录用户：" + loginVo);
                            throw new LoginException("没有访问权限");
                        }
                        if (StringUtils.isNotBlank(role)) {
                            if (roleCodes.contains(role)) {
                                log.error("没有访问权限的登录用户：" + loginVo);
                                throw new LoginException("该角色没有访问权限");
                            }
                        }
                        if (ArrayUtils.isNotEmpty(roles)) {
                            boolean containRole = false;
                            for (String roleCode : roleCodes) {
                                if (ArrayUtils.contains(roles, roleCode)) {
                                    containRole = true;
                                    break;
                                }
                            }
                            if (!containRole) {
                                log.error("没有访问权限的登录用户：" + loginVo);
                                throw new LoginException("该角色没有访问权限");
                            }
                        }
                    }
                }
            }
        } else if (PlatformType.MOBILE.getCode().equals(platformType)) {
            String token = TokenUtil.getToken(request);
            if (StringUtils.isBlank(token)) {
                throw new LoginTokenException("token不能为空");
            }
            if (token.length() != TokenLengthType.MOBILE.getLength()) {
                throw new LoginTokenException("无效token");
            }
            // 获取登录用户信息
            MemberLoginRedisVo loginVo = mobileLoginRedisService.getLoginRedisVo(token);
            if (loginVo == null) {
                throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
            }
        } else {
            throw new IllegalOperationException("请求违法!");
        }
        return true;
    }


    private boolean excludeRequest(HttpServletRequest request) {
        List<String> excludePaths = loginProperties.getExcludePaths();
        String path = request.getRequestURI();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        for (int i = 0; i < excludePaths.size(); i++) {
            if (antPathMatcher.match(excludePaths.get(i), path)) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }
}
